Ceci est un résumé de notre base de données produit : rangeIndex correspond au nombre de produits disponibles Columns correspondra aux différentes informations qu'on va avoir pour chaque produit, et dType au format de l'information : Ici on aura principalement des informations quantitatives et des informations catégorielles
<class 'pandas.core.frame.DataFrame'> RangeIndex: 320772 entries, 0 to 320771 Columns: 162 entries, code to water-hardness_100g dtypes: float64(106), object(56) memory usage: 396.5+ MB
Dans le contexte de notre étude, nous allons principalement travaillé sur les valeurs nutritionnelles.
Sur cette base de données, j'ai décidé de garder uniquement les informations nutritionnelles dont nous manquons cette information sur plus de 90% de produits. Une exception a été faire pour la valeur fruits-vegetables-nuts_100g qui est une information importante pour le calcul du nutriscore
Ensuite, je vous propose cette matrice de corrélation entre les différentes valeurs restantes
On voit que certaines valeurs nutritionnelles sont corrélées, et donc on pourrait s'imaginer d'en garder qu'un seul. De même, certaines valeurs sont corrélées à 1, ce qui signifie que les deux valeurs se comportent exactement de la même façon. Dans le cadre de notre analyse, on va se concentrer particulièrement sur les données relatives au nutriscore FR. Selon le site : https://quoidansmonassiette.fr/comment-est-calcule-le-nutri-score-logo-nutritionnel/ , le nutriscore est calculé à partir des données suivantes :
On va commencer par analyser les différentes valeurs que prend chaque valeurs nutritionnelles. Le but de cette exercice est surtout de pouvoir identifier rapidement les valeurs aberrantes qu'on décidera par la suite de supprimer.
| energy_100g | saturated-fat_100g | sugars_100g | salt_100g | fiber_100g | proteins_100g | fruits-vegetables-nuts_100g | nutrition-score-fr_100g | |
|---|---|---|---|---|---|---|---|---|
| count | 2.611130e+05 | 229554.000000 | 244971.000000 | 255510.000000 | 200886.000000 | 259922.000000 | 3036.000000 | 221210.000000 |
| mean | 1.141915e+03 | 5.129932 | 16.003484 | 2.028624 | 2.862111 | 7.075940 | 31.458587 | 9.165535 |
| std | 6.447154e+03 | 8.014238 | 22.327284 | 128.269454 | 12.867578 | 8.409054 | 31.967918 | 9.055903 |
| min | 0.000000e+00 | 0.000000 | -17.860000 | 0.000000 | -6.700000 | -800.000000 | 0.000000 | -15.000000 |
| 25% | 3.770000e+02 | 0.000000 | 1.300000 | 0.063500 | 0.000000 | 0.700000 | 0.000000 | 1.000000 |
| 50% | 1.100000e+03 | 1.790000 | 5.710000 | 0.581660 | 1.500000 | 4.760000 | 23.000000 | 10.000000 |
| 75% | 1.674000e+03 | 7.140000 | 24.000000 | 1.374140 | 3.600000 | 10.000000 | 51.000000 | 16.000000 |
| max | 3.251373e+06 | 550.000000 | 3520.000000 | 64312.800000 | 5380.000000 | 430.000000 | 100.000000 | 40.000000 |
On remarquera que nous allons avoir des valeurs d'énergie supérieures aux maximum connu dans la littérature (>3700kj), et des valeurs supérieures à 100g pour les différents nutriments sur un échantillon de 100g et on supprimera la ligne qui ont que des valeurs nulles. Cela nous donne un récap de notre dataframe comme le suivant :
| energy_100g | saturated-fat_100g | sugars_100g | salt_100g | fiber_100g | proteins_100g | fruits-vegetables-nuts_100g | nutrition-score-fr_100g | |
|---|---|---|---|---|---|---|---|---|
| count | 260219.000000 | 160815.000000 | 207875.000000 | 2.211780e+05 | 132048.000000 | 206287.000000 | 2074.000000 | 221210.000000 |
| mean | 1116.159059 | 7.316862 | 18.836645 | 1.833934e+00 | 4.308964 | 8.919932 | 46.050275 | 9.165535 |
| std | 784.412709 | 8.565257 | 21.775376 | 6.673844e+00 | 5.065973 | 8.210266 | 28.703994 | 9.055903 |
| min | 0.000000 | 0.000100 | 0.000100 | 5.000000e-08 | 0.000100 | 0.000100 | 1.000000 | -15.000000 |
| 25% | 372.000000 | 1.540000 | 3.200000 | 1.524000e-01 | 1.500000 | 3.330000 | 20.350000 | 1.000000 |
| 50% | 1096.000000 | 4.500000 | 9.100000 | 7.721600e-01 | 3.000000 | 6.670000 | 50.000000 | 10.000000 |
| 75% | 1674.000000 | 10.700000 | 28.950000 | 1.524000e+00 | 5.400000 | 12.100000 | 63.000000 | 16.000000 |
| max | 3699.000000 | 100.000000 | 100.000000 | 1.000000e+02 | 100.000000 | 100.000000 | 100.000000 | 40.000000 |
Regardons maintenant la répartition des différents produits afin d'y voir des corrélation entre les différents variables
On imputera les énergies manquantes par la moyenne, car la différence de groupe est non significative
Ensuite, on utilisera la méthode Iterative Imputer pour compléter notre base de données. La méthode IterativeImputer est une technique d'imputation de données manquantes qui utilise un modèle prédictif pour estimer les valeurs manquantes
Ci dessous le tableau descriptif de la base de donnée remplie avec notre technique d'imputation (1er) et le deuxième représente la description de la base de données avant la complétion
| energy_100g | saturated-fat_100g | sugars_100g | salt_100g | fiber_100g | proteins_100g | fruits-vegetables-nuts_100g | nutrition-score-fr_100g | |
|---|---|---|---|---|---|---|---|---|
| count | 262238.000000 | 262238.000000 | 262238.000000 | 262238.000000 | 262238.000000 | 262238.000000 | 262238.000000 | 262238.000000 |
| mean | 1116.159059 | 5.081455 | 16.836399 | 1.562115 | 3.978639 | 8.320228 | 22.744189 | 9.183769 |
| std | 781.387222 | 7.858952 | 21.381024 | 6.355384 | 4.370678 | 7.955714 | 37.732226 | 8.931618 |
| min | 0.000000 | -59.970830 | -174.631420 | -36.358693 | -25.768549 | -15.221512 | -426.269281 | -50.163780 |
| 25% | 380.000000 | 0.569588 | 3.170000 | 0.124460 | 1.565434 | 3.330000 | 11.019879 | 2.000000 |
| 50% | 1105.000000 | 3.043424 | 9.000000 | 0.761762 | 2.959765 | 6.394653 | 29.124140 | 9.000000 |
| 75% | 1674.000000 | 7.140000 | 25.237468 | 1.607820 | 5.133001 | 11.000000 | 45.390773 | 16.000000 |
| max | 3699.000000 | 100.000000 | 256.458209 | 100.000000 | 100.000000 | 100.000000 | 394.271543 | 58.460056 |
| energy_100g | saturated-fat_100g | sugars_100g | salt_100g | fiber_100g | proteins_100g | fruits-vegetables-nuts_100g | nutrition-score-fr_100g | |
|---|---|---|---|---|---|---|---|---|
| count | 262238.000000 | 160815.000000 | 207875.000000 | 2.211780e+05 | 132048.000000 | 206287.000000 | 2074.000000 | 221210.000000 |
| mean | 1116.159059 | 7.316862 | 18.836645 | 1.833934e+00 | 4.308964 | 8.919932 | 46.050275 | 9.165535 |
| std | 781.387222 | 8.565257 | 21.775376 | 6.673844e+00 | 5.065973 | 8.210266 | 28.703994 | 9.055903 |
| min | 0.000000 | 0.000100 | 0.000100 | 5.000000e-08 | 0.000100 | 0.000100 | 1.000000 | -15.000000 |
| 25% | 380.000000 | 1.540000 | 3.200000 | 1.524000e-01 | 1.500000 | 3.330000 | 20.350000 | 1.000000 |
| 50% | 1105.000000 | 4.500000 | 9.100000 | 7.721600e-01 | 3.000000 | 6.670000 | 50.000000 | 10.000000 |
| 75% | 1674.000000 | 10.700000 | 28.950000 | 1.524000e+00 | 5.400000 | 12.100000 | 63.000000 | 16.000000 |
| max | 3699.000000 | 100.000000 | 100.000000 | 1.000000e+02 | 100.000000 | 100.000000 | 100.000000 | 40.000000 |
On remarquera que cette méthode n'est clairement pas optimale, puisque des valeurs supérieures à 100 pour les sucres ou 40 pour le nutriscore ont été prédit. Mais en général, on va retrouver les mêmes moyennes globalement. Cela nous aidera uniquement à mieux comprendre .
L'Analyse en Composantes Principales (ACP) est une technique statistique largement utilisée pour transformer des données multidimensionnelles complexes en un ensemble de nouvelles variables, appelées "composantes principales" (F1,F2,F3...), qui capturent l'essentiel de la variabilité des données d'origine. L'objectif principal de l'ACP est de réduire la dimensionnalité des données tout en préservant autant d'informations que possible.
Pour commencer, on va d'abord remettre à l'échelle nos données. Cela permet de centrer les données autour de zéro et de leur donner une variance unitaire.
Notre tableau descriptif de notre base de donnée scalé. On remarquera que la moyenne de toutes les données est à 0 et l'écart type à 1
| energy_100g | saturated-fat_100g | sugars_100g | salt_100g | fiber_100g | proteins_100g | fruits-vegetables-nuts_100g | nutrition-score-fr_100g | |
|---|---|---|---|---|---|---|---|---|
| count | 262238.00 | 262238.00 | 262238.00 | 262238.00 | 262238.00 | 262238.00 | 262238.00 | 262238.00 |
| mean | 0.00 | -0.00 | -0.00 | -0.00 | 0.00 | 0.00 | 0.00 | -0.00 |
| std | 1.00 | 1.00 | 1.00 | 1.00 | 1.00 | 1.00 | 1.00 | 1.00 |
| min | -1.43 | -8.28 | -8.96 | -5.97 | -6.81 | -2.96 | -11.90 | -6.64 |
| 25% | -0.94 | -0.57 | -0.64 | -0.23 | -0.55 | -0.63 | -0.31 | -0.80 |
| 50% | -0.01 | -0.26 | -0.37 | -0.13 | -0.23 | -0.24 | 0.17 | -0.02 |
| 75% | 0.71 | 0.26 | 0.39 | 0.01 | 0.26 | 0.34 | 0.60 | 0.76 |
| max | 3.31 | 12.08 | 11.21 | 15.49 | 21.97 | 11.52 | 9.85 | 5.52 |
On instancie notre ACP et puis on l'entraine avec nos données scalés :
PCA()In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
PCA()
array([3.43951971e-01, 2.72504153e-01, 1.56389818e-01, 1.11136979e-01,
6.00696281e-02, 4.77616876e-02, 8.14247195e-03, 4.32921730e-05])
Intéressons nous maintenant à la variance captée par chaque nouvelle composante. La premi!ère composante (F1) capte 34% de la variance de nos données, La 2ème : 27% ... Cela signifie que la première composante explique 34% des informations qu'on a dans notre base de donnée initiale et ainsi de suite
array([ 34., 62., 77., 88., 94., 99., 100., 100.])
On a l'habitude en exploration de données de sommer ces variances, car plus on avance dans les composantes, et plus, nous détenons des informations
On voit ici que près de 75% de la variance (ou de l'information de nos données) est comprise dans les 3 premières composantes, et près de 90% dans les 4 premières. On remarque aussi que les 7 premières composantes définissent automatiquement la dernière.
On va à présent voir comment ces nouvelles composantes sont "calculées" à partir de nos valeurs nutritionnelles initiales
| F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8 | |
|---|---|---|---|---|---|---|---|---|
| energy_100g | 0.48 | -0.18 | 0.22 | 0.19 | -0.01 | -0.71 | 0.14 | -0.35 |
| saturated-fat_100g | 0.47 | -0.17 | -0.18 | 0.22 | 0.61 | 0.36 | 0.38 | 0.14 |
| sugars_100g | 0.21 | -0.51 | 0.21 | -0.19 | -0.61 | 0.34 | 0.36 | 0.03 |
| salt_100g | 0.01 | 0.41 | 0.43 | -0.64 | 0.24 | -0.01 | 0.41 | -0.10 |
| fiber_100g | 0.22 | 0.28 | 0.62 | 0.40 | -0.04 | 0.42 | -0.34 | -0.20 |
| proteins_100g | 0.30 | 0.54 | -0.08 | 0.20 | -0.36 | -0.16 | 0.22 | 0.61 |
| fruits-vegetables-nuts_100g | -0.32 | -0.36 | 0.54 | 0.15 | 0.25 | -0.22 | 0.01 | 0.59 |
| nutrition-score-fr_100g | 0.51 | -0.14 | -0.01 | -0.50 | 0.08 | -0.02 | -0.62 | 0.30 |
Alors, comment calcule t-on la première composante F1 ? et bien c'est assez simple : F1 = (0.48 energy_100g) + (0.47 saturated-fat_100g) + ... + (0.51 * nutrition-score-fr_100g)
et F2 ?
F2 = (-0.18 energy_100g) + (-0.17 saturated-fat_100g) + ... + (-0,14* nutrition-score-fr_100g)
Et ainsi de suite
Cette heatmap permet de visualiser aussi très rapidement le poids de chaque valeur sur chaque composante
<Axes: >
Maintenant, essayons de comprendre la corrélation entre ces différentes nouvelles composantes
<Figure size 640x480 with 0 Axes>
Sur les graphes précédents, on pourra étudier la projection des différents produits sur ces nouvelles composantes, un code couleur a été ajouté pour voir comment le code nutriscore se comporte selon les différentes situations et si on arrive à ressortir des clusters. J'ai ausssi ajouté un 3ème graphe qui représente le code couleur telle que c'est actuellment traité pour voir si le clustering est respectée telle que c'est représenté aujourdhui.